<?php

namespace app\api\admin\v1\service\finder;

use app\api\admin\v1\provider\CloudimagesProvider;
use app\api\admin\v1\provider\CloudappProvider;
use zhanshop\App;
use extend\docker\Container;
use extend\docker\Volume;
use zhanshop\util\MaxFile;
use zhanshop\server\Request;
use zhanshop\server\Response;

class Cloudapp extends BaseFinder
{
    protected $limits = [5000];
    protected $search = false;

    protected $headToolbar = [
        [
            'event' => 'add',
            'ico' => '&#xe608;',
            'title' => '创建',
            'method' => '',
            'page' => './page/docker/create-container.html',
        ],
        [
            'event' => 'deletes',
            'ico' => '&#xe640;',
            'title' => '删除',
            'method' => '',
            'page' => '',
        ],
    ];

    protected $rowToolbar = [
        [
            'event' => 'ajax',
            'ico' => '&#xe642;',
            'title' => '启动服务',
            'method' => 'start',
            'condition' => '[["running_state", "!=", "running"]]',
            'page' => '',
        ],
        [
            'event' => 'ajax',
            'ico' => '&#xe642;',
            'title' => '重启服务',
            'method' => 'restart',
            'page' => '',
        ],
        [
            'event' => 'ajax',
            'ico' => '&#xe642;',
            'title' => '停止服务',
            'method' => 'stop',
            'condition' => '[["running_state", "==", "running"], ["service_name", "!=", "zhanshop-cloud"]]',
            'page' => '',
        ],
        [
            'event' => 'edit',
            'ico' => '&#xe642;',
            'title' => '编辑服务',
            'method' => '',
            'condition' => '[["service_name", "!=", "zhanshop-cloud"]]',
            'page' => './page/docker/edit-container.html',
        ],
        [
            'event' => 'delete',
            'ico' => '&#xe642;',
            'title' => '删除服务',
            'method' => '',
            'condition' => '[["service_name", "!=", "zhanshop-cloud"]]',
            'page' => '',
        ],
        [
            'event' => 'open',
            'ico' => '&#xe642;',
            'title' => '服务日志',
            'method' => '',
            'field' => '',
            'page' => './page/docker/log-container.html',
        ],
        [
            'event' => 'open',
            'ico' => '&#xe642;',
            'title' => '资源统计',
            'method' => '',
            'field' => '',
            'page' => './page/docker/stats-container.html',
        ],
        [
            'event' => 'open',
            'ico' => '&#xe642;',
            'title' => '命令终端',
            'method' => '',
            'field' => '',
            'page' => './page/docker/shell-container.html',
        ],
    ];


    protected function getCols(string $schma){
        $cols = [
            'id' => array (
                'field' => 'id',
                'title' => 'ID',
                'input_type' => 'hidden',
                'type' => 'varchar',
                'width' => 130,
            ),
            'service_name' => array (
                'field' => 'service_name',
                'title' => '名称',
                'width' => 220,
                'input_type' => 'hidden',
                'type' => 'varchar',
                'null' => 'no',
            ),
            'running_state' => array (
                'field' => 'running_state',
                'title' => '运行状态',
                'width' => 88,
                'input_type' => 'hidden',
                'type' => 'varchar',
                'null' => 'no',
                'value' => []
            ),
            'internal_network' => array (
                'field' => 'internal_network',
                'title' => '内部访问',
                'input_type' => 'hidden',
                'type' => 'varchar',
                'null' => 'no',
                'width' => 150,
            ),
            'external_network' => array (
                'field' => 'external_network',
                'title' => '外部访问',
                'input_type' => 'hidden',
                'type' => 'varchar',
                'null' => 'no',
                'width' => 220,
            ),
            'start_time' => array (
                'field' => 'start_time',
                'title' => '启动时间',
                'width' => 160,
                'input_type' => 'hidden',
                'type' => 'varchar',
                'in_list' => true,
                'value' => []
            ),
            'description' => array (
                'field' => 'description',
                'title' => '描述',
                'width' => 280,
                'input_type' => 'hidden',
                'type' => 'varchar',
                'in_list' => true,
                'value' => []
            ),
            'create_time' => array (
                'field' => 'create_time',
                'title' => '创建时间',
                'width' => 170,
                'input_type' => 'time',
                'type' => 'varchar',
                'in_list' => true,
                'value' => []
            ),
        ];
        return $cols;
    }

    public function data(int $page, int $limit, array $order, array $search)
    {
        return App::make(CloudappProvider::class)->getList();
    }

    /**
     * 添加表单
     * @param Request $request
     * @param Response $response
     * @return array
     */
    public function addfrom(Request $request, Response $response)
    {
        $volumes = (new Volume())->getList();
        $data = [
            'images' => App::make(CloudimagesProvider::class)->getLists(),
            'volumes' => $volumes['volumes'] ?? [],
        ];
        return $data;
    }
    /**
     * 编辑表单
     * @param Request $request
     * @param Response $response
     * @return array
     */
    public function editfrom(Request $request, Response $response)
    {
        $input = $request->validateData($request->get(), [
            'pk' => 'Required',
        ])->getData();

        $detail = (new Container())->detail($input['pk']);
        if($detail['hostconfig']['networkmode'] != "docker_zhanshop-network") App::error()->setError("当前服务不支持编辑", 403);

        $envs = [];
        $envFile = App::runtimePath().'/cloud/env/'.str_replace('/', '', $detail['name']).'.json';
        if(file_exists($envFile)){
            $envs = json_decode(file_get_contents($envFile), true);
        }
        $detail['envs'] = $envs;
        $data = [
            'images' => App::make(CloudimagesProvider::class)->getLists(),
            'volumes' => (new Volume())->getList()['volumes'],
            'data' => $detail,
        ];
        return $data;
    }

    /**
     * 添加
     * @param Request $request
     * @return void
     */
    public function post(Request $request, Response $response){
        $post = $request->post();
        $envs = [];
        foreach($post['images']['env']['val'] as $k => $v){
            if($v){
                $key = $post['images']['env']['key'][$k];
                if($key) $envs[$key] = $v;
            }
        }
        // 环境变量记录到文件中
        if($envs) file_put_contents(App::runtimePath().'/cloud/env/'.$post['base']['name'].'.json', json_encode($envs));
        $ports = [];
        foreach($post['images']['external_port'] as $k => $v){
            if($v){
                $internalPort = $post['images']['internal_port'][$k];
                if($internalPort) $ports[] = $v.':'.$internalPort.'/'.$post['images']['protocol'][$k];
            }
        }
        $volumes = [];
        $mounts = [];
        foreach($post['volume']['mount'] as $k => $v){
            if($v){
                $type = $post['volume']['type'][$k] ?? 'bind';
                $name = $post['volume']['name'][$k];
                if($name == false) continue;
                if($type == 'volume'){
                    $volumes[] = $name.':'.$v;
                }else{
                    if (preg_match('/[A-Z]/', $name)) {
                        App::error()->setError($name.'源路径中不支持大写字母', 400);
                    }
                    $mounts[] = [
                        'Type' => 'bind',
                        'Source' => $name,
                        'Target' => $v,
                        'ReadOnly' => false
                    ];
                }
            }
        }
        $volumes = ['volumes' => $volumes, 'mounts' => $mounts];
        if(preg_match('/^[a-zA-Z][a-zA-Z0-9_-]*$/', $post['base']['name']) == false) App::error()->setError('服务名称仅能字母开头后可包含字母数字和下划线');
        if(isset($post['images']['config']) && $post['images']['config']){
            $post['images']['config'] = json_decode($post['images']['config'], true);
        }
        $container = (new Container())->create($post['base']['name'], $post['images']['name'], $post['base']['description'], $post['base']['version'], $post['images']["cmd"], $envs, $ports, $volumes, intval($post['images']['cpu']), intval($post['images']['ram']), intval($post['images']['gpu']), $post['images']['config'] ?? []);
        if($container && isset($container['id'])){
            $id = substr($container['id'], 0, 12);
            (new Container())->start($id);
        }
        return $container;
    }

    public function put(Request $request, Response $response){
        $id = $request->get('pk');
        // 判断容器名称是否发生变化
        $oldName = str_replace('/', '', (new Container())->detail($id)['name']);
        $post = $request->post();
        if(preg_match('/^[a-zA-Z][a-zA-Z0-9_-]*$/', $post['base']['name']) == false) App::error()->setError('服务名称仅能字母开头后可包含字母数字和下划线');
        foreach($post['volume']['mount'] as $k => $v){
            if($v){
                $type = $post['volume']['type'][$k] ?? 'bind';
                $name = $post['volume']['name'][$k];
                if($name == false) continue;
                if($type != 'volume'){
                    if (preg_match('/[A-Z]/', $name)) {
                        App::error()->setError($name.'源路径中不支持大写字母', 400);
                    }
                }
            }
        }

        if($oldName != $post['base']['name']){
            // 删除nginx配置文件
            Cloudlvs::proxyHostDelete($oldName);
        }
        (new Container())->stop($id);
        (new Container())->delete($id);
        return $this->post($request, $response);
    }

    public function start(Request $request, Response $response)
    {
        $id = $request->param('id');
        (new Container())->start($id);
        return (new CloudappProvider())->getList($id);
    }

    public function restart(Request $request, Response $response)
    {
        $id = $request->param('id');
        (new Container())->restart($id);
        return (new CloudappProvider())->getList($id);
    }

    public function stop(Request $request, Response $response)
    {
        $id = $request->param('id');
        (new Container())->stop($id);
        return (new CloudappProvider())->getList($id);
    }

    public function stats(Request $request, Response $response)
    {
        $id = $request->param('id');
        $date = $request->get('date', date('Ymd'));
        $statsPath = App::runtimePath().'/cloud/stats/'.$id.'-'.$date.'.log';
        $data = [
            'cpu' => [
                'time' => [],
                'val' => [],
            ],
            'ram' => [
                'time' => [],
                'val' => [],
            ],
            'network' => [
                'time' => [],
                'val' => [],
            ],
        ];
        if(file_exists($statsPath)){
            $statsData = file_get_contents($statsPath);
            $stats = explode("\n", $statsData);
            foreach($stats as $k => $v){
                $arr = explode("###", $v);
                if(isset($arr[1])){
                    $json = json_decode($arr[1], true);
                    $data['cpu']['time'][] = $arr[0];

                    //////////CPU使用率///////////
                    $cpuDelta = ($json['cpu_stats']['cpu_usage']['total_usage'] ?? 0) - ($json['precpu_stats']['cpu_usage']['total_usage'] ?? 0);
                    $numberCpus = ($json['cpu_stats']['online_cpus'] ?? 0);
                    $systemCpuDelta = ($json['cpu_stats']['system_cpu_usage'] ?? 0) - ($json['precpu_stats']['cpu_usage']['total_usage'] ?? 0);
                    try {
                        $cpuUsageRate = ($cpuDelta / $systemCpuDelta) * $numberCpus * 100.0;
                        $data['cpu']['val'][] = round($cpuUsageRate, 4);
                    }catch (\Throwable $err){
                        $data['cpu']['val'][] = -1;
                    }

                    //////////CPU使用率///////////
                    $data['ram']['time'][] = $arr[0];

                    //////////内存使用量///////////
                    $data['ram']['val'][] = ($json['memory_stats']['usage'] ?? 0) - ($json['memory_stats']['stats']['inactive_file'] ?? 0) - ($json['memory_stats']['stats']['active_file'] ?? 0);
                    //////////内存使用量///////////

                    $data['network']['time'][] = $arr[0];
                    $data['network']['val'][] = round(($json['networks']['eth0']['tx_bytes'] ?? 0) / 1024 / 1024 ?? 0, 2);
                }

            }
        }
        $statsData = [
            'dates' => [date('Ymd'), date('Ymd', strtotime('-1 day')),date('Ymd', strtotime('-2 day')),date('Ymd', strtotime('-3 day')),date('Ymd', strtotime('-4 day')),date('Ymd', strtotime('-5 day')),date('Ymd', strtotime('-6 day')),date('Ymd', strtotime('-7 day')),date('Ymd', strtotime('-8 day')),date('Ymd', strtotime('-9 day')),date('Ymd', strtotime('-10 day')),date('Ymd', strtotime('-11 day')),date('Ymd', strtotime('-12 day')),date('Ymd', strtotime('-13 day')),date('Ymd', strtotime('-14 day'))],
            'data' => $data,
        ];
        return $statsData;
    }

    public function delete(Request $request, Response $response){
        $pk = $request->param("pk");
        $isRestart = false;
        $apps = (new CloudappProvider())->getList();
        foreach ($pk as $id){
            (new Container())->stop($id);
            (new Container())->delete($id);
            foreach($apps['list'] as $app){
                if($app['id'] == $id){
                    Cloudlvs::proxyHostDelete($app['service_name']);
                    $isRestart = true;
                }
            }
        }
        if($isRestart) Cloudlvs::restartNginx();
        return [];
    }

    public function log(Request $request, Response $response)
    {
        $id = $request->get('id');
        $containers = glob('/var/lib/docker/containers/'.$id.'*');
        $containerId = $containers[0] ?? App::error()->setError("选择的容器id不存在");
        $basename = basename($containerId);
        $logPath = $containerId.'/'.$basename.'-json.log';
        $logs = "";
        $line = 0;
        App::make(MaxFile::class)->read($logPath, function ($msg) use (&$logs, &$line){
            $logs .= $msg;
            if($line > 10000) return false;
        });
        return [
            'log' => $logs,
            'path' => $logPath
        ];
    }

    public function resetlog(Request $request, Response $response)
    {
        $id = $request->get('id');
        $containers = glob('/var/lib/docker/containers/'.$id.'*');
        $containerId = $containers[0] ?? App::error()->setError("选择的容器id不存在");
        $basename = basename($containerId);
        $logPath = $containerId.'/'.$basename.'-json.log';
        if(file_exists($logPath)){
            file_put_contents($logPath, "");
        }
    }
}